package avicit.bdp.dds.api.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.dto.EnumDto;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.DateUtils;
import avicit.bdp.common.utils.EnumUtil;
import avicit.bdp.dds.dispatch.enums.DependResult;
import avicit.bdp.dds.dispatch.enums.ExecutionStatus;
import avicit.bdp.dds.dispatch.enums.Flag;
import avicit.bdp.dds.dispatch.enums.TaskStateType;
import avicit.bdp.dds.dispatch.enums.TaskType;
import avicit.bdp.dds.dispatch.model.ProcessData;
import avicit.bdp.dds.dispatch.model.TaskNode;
import avicit.bdp.dds.dispatch.model.TaskNodeRelation;
import avicit.bdp.dds.dispatch.process.ProcessDag;
import avicit.bdp.dds.dispatch.process.Property;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.api.dto.StateDto;
import avicit.bdp.dds.api.dto.gantt.GanttDto;
import avicit.bdp.dds.api.dto.gantt.Task;
import avicit.bdp.dds.api.enums.ExecuteStatus;
import avicit.bdp.dds.api.enums.ProcessType;
import avicit.bdp.dds.api.enums.Status;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.common.graph.DAG;
import avicit.bdp.dds.common.utils.ParameterUtils;
import avicit.bdp.dds.common.utils.placeholder.BusinessTimeUtils;
import avicit.bdp.dds.dao.entity.ProcessDefinition;
import avicit.bdp.dds.dao.entity.ProcessInstance;
import avicit.bdp.dds.dao.entity.ProcessInstanceMap;
import avicit.bdp.dds.dao.entity.ProcessResourceSettings;
import avicit.bdp.dds.dao.entity.Project;
import avicit.bdp.dds.dao.entity.TaskInstance;
import avicit.bdp.dds.dao.mapper.ProcessInstanceMapper;
import avicit.bdp.dds.dao.mapper.ProjectMapper;
import avicit.bdp.dds.dao.utils.DagHelper;
import avicit.bdp.dds.service.process.ProcessService;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.rest.msg.QueryRespBean;
import avicit.platform6.core.rest.msg.ResponseMsg;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mayanj
 * process instance service
 */
@Service
public class ProcessInstanceService extends BaseService<ProcessInstanceMapper, ProcessInstance> {

    private final ProcessService processService;
    private final ProcessDefinitionService processDefinitionService;
    private final ProcessInstanceMapService processInstanceMapService;
    private final TaskInstanceService taskInstanceService;
    private final ProjectMapper projectMapper;
    private final LoggerService loggerService;
    private final ProcessResourceConfigService processResourceConfigService;

    //运行的状态（提交成功、运行中、等待线程、等待依赖加载）
    private static final Integer[] RUNNING_STATUS = {0, 1, 10, 11};
    //停止状态
    private static final Integer[] STOP_STATUS = {2, 3, 4, 5 ,9};
    //失败状态
    private static final Integer[] FAIL_STATUS = {6, 8};
    //成功状态
    private static final Integer[] SUCCESS_STATUS = {7};

    @Autowired
    public ProcessInstanceService(ProcessService processService,
                                  ProcessDefinitionService processDefinitionService,
                                  ProcessInstanceMapService processInstanceMapService,
                                  TaskInstanceService taskInstanceService,
                                  ProjectMapper projectMapper,
                                  LoggerService loggerService,
                                  ProcessResourceConfigService processResourceConfigService) {

        this.processService = processService;
        this.processDefinitionService = processDefinitionService;
        this.processInstanceMapService = processInstanceMapService;
        this.taskInstanceService = taskInstanceService;
        this.projectMapper = projectMapper;
        this.loggerService = loggerService;
        this.processResourceConfigService = processResourceConfigService;
    }

    /**
     * query process instance by id
     *
     * @param processId process instance id
     * @return process instance detail
     */
    public ProcessInstance queryProcessInstanceById(String processDefinitionId, String processId) {
        ProcessInstance instance;
        if (StringUtils.isNotBlank(processId)) {
            instance = processService.findProcessInstanceDetailById(processId);
        } else {
            instance = this.queryLastOneProcess(processDefinitionId);
            if (instance == null) {
                ProcessDefinition definition = processDefinitionService.selectByPrimaryKey(processDefinitionId);
                instance = new ProcessInstance();
                instance.setId(ComUtil.getId());
                instance.setProcessInstanceJson(definition.getProcessDefinitionJson());
                instance.setLocations(definition.getLocations());
                instance.setConnects(definition.getConnects());
                instance.setGlobalParams(definition.getGlobalParams());
                instance.setProcessDefinitionId(definition.getId());
            }
        }
        BdpLogUtil.log4Query(instance);
        return instance;
    }

    /**
     * paging query process instance list, filtering according to project, process definition, time range, keyword, process status
     *
     * @param pageNo          page number
     * @param pageSize        page size
     * @param processDefineId process definition id
     * @param searchVal       search value
     * @param stateType       state type
     * @param host            host
     * @param startDate       start time
     * @param endDate         end time
     * @return process instance list
     */
    public QueryRespBean<ProcessInstance> queryProcessInstanceList(String processDefineId, String startDate,
                                                                   String endDate, String searchVal,
                                                                   ExecutionStatus stateType, String host,
                                                                   Integer pageNo, Integer pageSize) {
        QueryRespBean<ProcessInstance> queryRespBean = new QueryRespBean<>();

        PageHelper.startPage(pageNo, pageSize);

        // filter by state
        int[] statusArray = null;
        if (stateType != null) {
            statusArray = new int[]{stateType.ordinal()};
        }

        Date start = null;
        Date end = null;
        try {
            if (StringUtils.isNotEmpty(startDate)) {
                start = DateUtils.getScheduleDate(startDate);
            }
            if (StringUtils.isNotEmpty(endDate)) {
                end = DateUtils.getScheduleDate(endDate);
            }
        } catch (Exception e) {
            throw new BusinessException(MessageFormat.format(Status.REQUEST_PARAMS_NOT_VALID_ERROR.getMsg(),
                    "startDate,endDate"));
        }

        Page<ProcessInstance> processInstanceList = mapper.queryProcessInstanceListPaging(processDefineId, searchVal,
                null, statusArray, host, start, end);

        List<ProcessInstance> processInstances = processInstanceList.getResult();
        for (ProcessInstance processInstance : processInstances) {
            processInstance.setDuration(DateUtils.differSec(processInstance.getStartTime(), processInstance.getEndTime()));
        }

        queryRespBean.setResult(processInstanceList);
        BdpLogUtil.log4Query(queryRespBean);
        return queryRespBean;
    }

    /**
     * query task list by process instance id
     *
     * @param processId process instance id
     * @return task list for the process instance
     * @throws IOException io exception
     */
    public List<TaskInstance> queryTaskListByProcessId(String processId) throws IOException {
        List<TaskInstance> taskInstanceList = processService.findValidTaskListByProcessId(processId);
        addDependResultForTaskList(taskInstanceList);
        return taskInstanceList;
    }

    /**
     * add dependent result for dependent task
     *
     * @param taskInstanceList taskInstanceList
     */
    private void addDependResultForTaskList(List<TaskInstance> taskInstanceList) throws IOException {
        for (TaskInstance taskInstance : taskInstanceList) {
            if (taskInstance.getTaskType().equalsIgnoreCase(TaskType.DEPENDENT.toString())) {
                ResponseMsg<String> logResult = loggerService.queryLog(taskInstance.getId(), 0, 4098);
                if ("200".equals(logResult.getRetCode())) {
                    String log = logResult.getResponseBody();
                    Map<String, DependResult> resultMap = parseLogForDependentResult(log);
                    taskInstance.setDependentResult(JSONUtils.toJson(resultMap));
                }
            }
        }
    }

    private Map<String, DependResult> parseLogForDependentResult(String log) throws IOException {
        Map<String, DependResult> resultMap = new HashMap<>(16);
        if (StringUtils.isEmpty(log)) {
            return resultMap;
        }

        BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(log.getBytes(
                StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
        String line;
        while ((line = br.readLine()) != null) {
            if (line.contains(Constants.DEPENDENT_SPLIT)) {
                String[] tmpStringArray = line.split(":\\|\\|");
                if (tmpStringArray.length != 2) {
                    continue;
                }
                String dependResultString = tmpStringArray[1];
                String[] dependStringArray = dependResultString.split(Constants.COMMA);
                if (dependStringArray.length != 2) {
                    continue;
                }
                String key = dependStringArray[0].trim();
                DependResult dependResult = DependResult.valueOf(dependStringArray[1].trim());
                resultMap.put(key, dependResult);
            }
        }
        return resultMap;
    }

    /**
     * query sub process instance detail info by task id
     *
     * @param taskId task id
     * @return sub process instance detail
     */
    public String querySubProcessInstanceByTaskId(String taskId) {
        TaskInstance taskInstance = processService.findTaskInstanceById(taskId);
        if (taskInstance == null) {
            throw new BusinessException(MessageFormat.format(Status.TASK_INSTANCE_NOT_EXISTS.getMsg(), taskId));
        }
        if (!taskInstance.isSubProcess()) {
            throw new BusinessException(MessageFormat.format(Status.TASK_INSTANCE_NOT_SUB_WORKFLOW_INSTANCE.getMsg(),
                    taskInstance.getName()));
        }

        ProcessInstance subWorkflowInstance = processService.findSubProcessInstance(
                taskInstance.getProcessInstanceId(), taskInstance.getId());
        if (subWorkflowInstance == null) {
            throw new BusinessException(MessageFormat.format(Status.SUB_PROCESS_INSTANCE_NOT_EXIST.getMsg(), taskId));
        }
        return subWorkflowInstance.getId();
    }

    /**
     * update process instance
     *
     * @param processInstanceJson process instance json
     * @param processInstanceId   process instance id
     * @param scheduleTime        schedule time
     * @param syncDefine          sync define
     * @param locations           locations
     * @param connects            connects
     * @return update result code
     */
    public boolean updateProcessInstance(String processInstanceId, String processInstanceJson,
                                         String scheduleTime, Boolean syncDefine,
                                         String locations, String connects) {
        //check process instance exists
        ProcessInstance processInstance = processService.findProcessInstanceDetailById(processInstanceId);
        if (processInstance == null) {
            throw new BusinessException(MessageFormat.format(Status.PROCESS_INSTANCE_NOT_EXIST.getMsg(), processInstanceId));
        }

        //check process instance status
        if (!ExecutionStatus.of(processInstance.getState()).typeIsFinished()) {
            throw new BusinessException(MessageFormat.format(Status.PROCESS_INSTANCE_STATE_OPERATION_ERROR.getMsg(), "update"));
        }
        Date schedule;
        if (scheduleTime != null) {
            schedule = DateUtils.getScheduleDate(scheduleTime);
        } else {
            schedule = processInstance.getScheduleTime();
        }
        processInstance.setScheduleTime(schedule);
        processInstance.setLocations(locations);
        processInstance.setConnects(connects);
        String globalParams = null;
        String originDefParams = null;
        int timeout = processInstance.getTimeout();
        ProcessDefinition processDefinition = processService.findProcessDefineById(processInstance.getProcessDefinitionId());
        if (StringUtils.isNotEmpty(processInstanceJson)) {
            ProcessData processData = JSONUtils.parseObject(processInstanceJson, ProcessData.class);
            //check workflow json is valid
            try {
                processDefinitionService.checkProcessNodeList(processData);
            } catch (Exception e) {
                logger.error("流程节点json校验失败：" + e.getMessage());
                return false;
            }

            if (processData != null) {
                originDefParams = JSONUtils.toJson(processData.getGlobalParams());
                List<Property> globalParamList = processData.getGlobalParams();
                Map<String, String> globalParamMap = globalParamList.stream().collect(Collectors.toMap(Property::getProp, Property::getValue));
                globalParams = ParameterUtils.curingGlobalParams(globalParamMap, globalParamList,
                        processInstance.getCmdTypeIfComplement(), schedule);
                timeout = processData.getTimeout();
            }

            processInstance.setTimeout(timeout);
            String tenant = processService.getTenantForProcess();
            processInstance.setTenantCode(tenant);
            processInstance.setProcessInstanceJson(processInstanceJson);
            processInstance.setGlobalParams(globalParams);
        }

        int update = processService.updateProcessInstance(processInstance);
        int updateDefine = 1;
        if (Boolean.TRUE.equals(syncDefine) && StringUtils.isNotEmpty(processInstanceJson)) {
            processDefinition.setProcessDefinitionJson(processInstanceJson);
            processDefinition.setGlobalParams(originDefParams);
            processDefinition.setLocations(locations);
            processDefinition.setConnects(connects);
            processDefinition.setTimeout(timeout);
            updateDefine = processDefinitionService.updateByPrimaryKey(processDefinition);
        }
        if (update > 0 && updateDefine > 0) {
            //记录日志
            BdpLogUtil.log4Update(processInstance, processDefinition);
            return true;
        } else {
            throw new BusinessException(Status.UPDATE_PROCESS_INSTANCE_ERROR.getMsg());
        }
    }

    /**
     * query parent process instance detail info by sub process instance id
     *
     * @param subId sub process id
     * @return parent instance detail
     */
    public String queryParentInstanceBySubId(String subId) {

        ProcessInstance subInstance = processService.findProcessInstanceDetailById(subId);
        if (subInstance == null) {
            throw new BusinessException(MessageFormat.format(Status.PROCESS_INSTANCE_NOT_EXIST.getMsg(), subId));
        }
        if (subInstance.getIsSubProcess() == Flag.NO.getCode()) {
            throw new BusinessException(MessageFormat.format(Status.PROCESS_INSTANCE_NOT_SUB_PROCESS_INSTANCE.getMsg(),
                    subInstance.getName()));
        }

        ProcessInstance parentWorkflowInstance = processService.findParentProcessInstance(subId);
        if (parentWorkflowInstance == null) {
            throw new BusinessException(Status.SUB_PROCESS_INSTANCE_NOT_EXIST.getMsg());
        }
        return parentWorkflowInstance.getId();
    }

    /**
     * delete process instance by id, at the same time，delete task instance and their mapping relation data
     *
     * @param processInstanceId process instance id
     * @return delete result code
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteProcessInstanceById(String processInstanceId) {

        ProcessInstance processInstance = processService.findProcessInstanceDetailById(processInstanceId);
        if (null == processInstance) {
            throw new BusinessException(MessageFormat.format(Status.PROCESS_INSTANCE_NOT_EXIST.getMsg(), processInstanceId));
        }

        processService.removeTaskLogFile(processInstanceId);
        // delete database cascade
        int delete = processService.deleteWorkProcessInstanceById(processInstanceId);

        processService.deleteAllSubWorkProcessByParentId(processInstanceId);
        processService.deleteWorkProcessMapByParentId(processInstanceId);

        if (delete > 0) {
            //记录日志
            BdpLogUtil.log4Delete(processInstance);
            return true;
        } else {
            throw new BusinessException(Status.DELETE_PROCESS_INSTANCE_BY_ID_ERROR.getMsg());
        }
    }

    /**
     * view process instance variables
     *
     * @param processInstanceId process instance id
     * @return variables data
     */
    public Map<String, Object> viewVariables(String processInstanceId) {
        ProcessInstance processInstance = mapper.selectByPrimaryKey(processInstanceId);

        if (processInstance == null) {
            throw new BusinessException("workflow instance is null");
        }

        Map<String, String> timeParams = BusinessTimeUtils.getBusinessTime(processInstance.getCmdTypeIfComplement(),
                processInstance.getScheduleTime());

        String userDefinedParams = processInstance.getGlobalParams();

        // global params
        List<Property> globalParams = new ArrayList<>();

        if (userDefinedParams != null && userDefinedParams.length() > 0) {
            globalParams = JSON.parseArray(userDefinedParams, Property.class);
        }

        // global param string
        String globalParamStr = JSON.toJSONString(globalParams);
        globalParamStr = ParameterUtils.convertParameterPlaceholders(globalParamStr, timeParams);
        globalParams = JSON.parseArray(globalParamStr, Property.class);
        for (Property property : globalParams) {
            timeParams.put(property.getProp(), property.getValue());
        }

        Map<String, Map<String, Object>> localUserDefParams = new HashMap<>(16);

        String workflowInstanceJson = processInstance.getProcessInstanceJson();
        ProcessData workflowData = JSONUtils.parseObject(workflowInstanceJson, ProcessData.class);
        if (workflowData != null) {
            List<TaskNode> taskNodeList = workflowData.getTasks();
            // local params

            for (TaskNode taskNode : taskNodeList) {
                String parameter = taskNode.getParams();
                Map<String, String> map = JSONUtils.toMap(parameter);
                if (map != null && !map.isEmpty()) {
                    String localParams = map.get(Constants.LOCAL_PARAMS);
                    if (localParams != null && !localParams.isEmpty()) {
                        localParams = ParameterUtils.convertParameterPlaceholders(localParams, timeParams);
                        List<Property> localParamsList = JSON.parseArray(localParams, Property.class);
                        Map<String, Object> localParamsMap = new HashMap<>(4);
                        localParamsMap.put("taskType", taskNode.getType());
                        localParamsMap.put("localParamsList", localParamsList);
                        if (CollectionUtils.isNotEmpty(localParamsList)) {
                            localUserDefParams.put(taskNode.getName(), localParamsMap);
                        }
                    }
                }
            }
        }

        Map<String, Object> resultMap = new HashMap<>(2);
        resultMap.put(Constants.GLOBAL_PARAMS, globalParams);
        resultMap.put(Constants.LOCAL_PARAMS, localUserDefParams);
        return resultMap;
    }

    /**
     * query process instance by processDefinitionId
     *
     * @param processDefinitionId processDefinitionId
     * @param size                size
     * @return process instance list
     */
    List<ProcessInstance> queryByProcessDefineId(String processDefinitionId, int size) {
        return this.mapper.queryByProcessDefineId(processDefinitionId, size);
    }

    /**
     * 项目监控
     *
     * @param projectId      projectId
     * @param definitionId   definitionId
     * @param instanceName   instanceName
     * @param startTime      startTime
     * @param endTime        endTime
     * @param instanceStatue instanceStatue
     * @param pageNo         pageNo
     * @param pageSize       pageSize
     */
    @Transactional(readOnly = true)
    public QueryRespBean<ProcessInstance> getListForControl(String projectId, String definitionId, String instanceName,
                                                            String startTime, String endTime,
                                                            Integer instanceStatue, Integer type,
                                                            Integer pageNo, Integer pageSize) {
        QueryRespBean<ProcessInstance> queryRespBean = new QueryRespBean<>();

        //处理状态
        List<Integer> statusList = new ArrayList<>();
        if (instanceStatue != null){
            statusList = handleInstanceStatus(instanceStatue);
        }
        try {
            //没有项目id和流程id 查询总体监控
            if (StringUtils.isBlank(definitionId)) {
                if (StringUtils.isBlank(projectId)){
                    throw new BusinessException("projectId参数为空，请检查！");
                }
                com.github.pagehelper.PageHelper.startPage(pageNo, pageSize);
                //(查询type = 6789)
                Page<ProcessInstance> dataList = this.mapper.getListForTotalControl(projectId, instanceName,
                        startTime, endTime, type, statusList);
                List<ProcessInstance> list = dataList.getResult();
                if (list != null && list.size() > 0) {
                    for (ProcessInstance processInstance : list) {
                        //处理运行状态
                        Integer newStatus = handleReturnStatus(processInstance.getState());
                        processInstance.setState(newStatus);

                        long endRunTime = processInstance.getEndTime() == null ? System.currentTimeMillis() : processInstance.getEndTime().getTime();
                        long duration = (endRunTime - processInstance.getStartTime().getTime()) / 1000;
                        processInstance.setDuration(duration);
                        String formatTime = DateUtils.formatTimeLong(duration);
                        processInstance.setFormatTime(formatTime);
                    }
                }

                queryRespBean.setResult(dataList);
            } else {
                com.github.pagehelper.PageHelper.startPage(pageNo, pageSize);
                Page<ProcessInstance> dataList = this.mapper.getListForControl(projectId, definitionId, instanceName,
                        startTime, endTime, ProcessType.DATA_DEVELOP.getCode(), statusList);
                List<ProcessInstance> list = dataList.getResult();
                if (list != null && list.size() > 0) {
                    for (ProcessInstance processInstance : list) {
                        //处理运行状态
                        Integer newStatus = handleReturnStatus(processInstance.getState());
                        processInstance.setState(newStatus);

                        long endRunTime = processInstance.getEndTime() == null ? System.currentTimeMillis() : processInstance.getEndTime().getTime();
                        long duration = (endRunTime - processInstance.getStartTime().getTime()) / 1000;
                        processInstance.setDuration(duration);
                        String formatTime = DateUtils.formatTimeLong(duration);
                        processInstance.setFormatTime(formatTime);
                    }
                }
                queryRespBean.setResult(dataList);
            }
            BdpLogUtil.log4Query(queryRespBean);
            return queryRespBean;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage(), e);
        }
    }

    private Integer handleReturnStatus(Integer state) {
        if(Arrays.asList(RUNNING_STATUS).contains(state)){
            return 1;
        }else if(Arrays.asList(STOP_STATUS).contains(state)){
            return 5;
        }else if(Arrays.asList(FAIL_STATUS).contains(state)){
            return 6;
        }else {
            return 7;
        }
    }

    //处理任务运行状态（任务监控）
    private List<Integer> handleInstanceStatus(Integer instanceStatue) {

        ExecuteStatus status = ExecuteStatus.of(instanceStatue);
        switch (status){
            case RUNNING_EXECUTION:
                return Arrays.asList(RUNNING_STATUS);
            case STOP:
                return Arrays.asList(STOP_STATUS);
            case SUCCESS:
                return Arrays.asList(SUCCESS_STATUS);
            case FAILURE:
                return Arrays.asList(FAIL_STATUS);
            default:
                return null;
        }
    }

    /**
     * 查询正在运行的节点信息
     *
     * @param processInstance     流程实例
     * @param processDefinitionId 流程定义ID
     * @param currentTime         当前时间
     */
    private void handleProcessInstance(ProcessInstance processInstance, String processDefinitionId, int currentTime) {
        Long allNoteCount = taskInstanceService.selectAllNoteCount(processDefinitionId, processInstance.getId());
        Long successCount = taskInstanceService.selectSuccessCount(processDefinitionId, processInstance.getId());
        processInstance.setNodeRate(successCount.toString() + "/" + allNoteCount.toString());
        List<TaskInstance> taskInstances = taskInstanceService.selectListByInstanceId(processDefinitionId, processInstance.getId(), TaskStateType.RUNNING.ordinal(), null);
        if (taskInstances != null && taskInstances.size() > 0) {
            for (TaskInstance taskInstance : taskInstances) {
                taskInstance.setInstanceStartTime(processInstance.getStartTime());
                int beginTime = (int) (taskInstance.getStartTime().getTime() / 1000);
                taskInstance.setRunTimes(currentTime - beginTime);
                int instanceBeginTime = (int) (processInstance.getStartTime().getTime() / 1000);
                taskInstance.setInstanceRunTimes(currentTime - instanceBeginTime);
            }
            processInstance.setTaskInstanceList(taskInstances);
        }
    }

    /**
     * 获取甘特图
     *
     * @param processInstanceId 流程实例ID
     * @return 甘特图
     */
    public Map<String, Object> getGantt(String processInstanceId) {
        Map<String, Object> result = new HashMap<>();

        if (StringUtils.isBlank(processInstanceId)) {
            throw new BusinessException("参数为空");
        }
        try {
            ProcessInstance processInstance = this.selectByPrimaryKey(processInstanceId);

            if (processInstance == null) {
                throw new BusinessException("数据不存在");
            }

            GanttDto ganttDto = new GanttDto();

            DAG<String, TaskNode, TaskNodeRelation> dag = processInstance2DAG(processInstance);
            //topological sort
            List<String> nodeList = dag.topologicalSort();

            ganttDto.setTaskNames(nodeList);

            List<Task> taskList = new ArrayList<>();
            for (String node : nodeList) {
                TaskInstance taskInstance = taskInstanceService.queryByInstanceIdAndName(processInstanceId, node);
                if (taskInstance == null) {
                    continue;
                }
                Date startTime = taskInstance.getStartTime() == null ? new Date() : taskInstance.getStartTime();
                Date endTime = taskInstance.getEndTime() == null ? new Date() : taskInstance.getEndTime();
                Task task = new Task();
                task.setTaskName(taskInstance.getName());
                task.getStartDate().add(startTime.getTime());
                task.getEndDate().add(endTime.getTime());
                task.setIsoStart(startTime);
                task.setIsoEnd(endTime);
                task.setStatus(taskInstance.getState().toString());
                task.setExecutionDate(taskInstance.getStartTime());
                task.setDuration(DateUtils.format2Readable(endTime.getTime() - startTime.getTime()));
                taskList.add(task);
            }
            ganttDto.setTasks(taskList);

            result.put("data", ganttDto);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage(), e);
        }
        return result;
    }

    /**
     * process instance to DAG
     *
     * @param processInstance input process instance
     * @return process instance dag.
     */
    private DAG<String, TaskNode, TaskNodeRelation> processInstance2DAG(ProcessInstance processInstance) {
        String processDefinitionJson = processInstance.getProcessInstanceJson();
        ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class);
        List<ProcessResourceSettings> processResourceConfigs = processResourceConfigService.getByProcessDefinitionId(processInstance.getProcessDefinitionId());
        List<TaskNode> taskNodeList = processData.getTasks();
        ProcessDag processDag = DagHelper.getProcessDag(taskNodeList);
        return DagHelper.buildDagGraph(processDag, processResourceConfigs);
    }

    /**
     * 监控列表获取状态列表
     *
     * @return 状态列表
     */
    public List<EnumDto> getStateList() {
        return EnumUtil.getEnumList(ExecuteStatus.class);
    }

    /**
     * 获取各个流程成功实例数
     *
     * @param yesterday 昨日
     * @param state     状态
     * @return 各个流程成功实例数
     */
    public List<StateDto> getDataCount(String yesterday, Integer state) {
        return this.mapper.getDataCount(yesterday, state);
    }

    /**
     * 获取今天所有数据
     *
     * @param today         今日
     * @param projectIdList 项目ID
     * @param state         状态
     * @return 今天所有数据
     */
    public List<StateDto> getTodayDataAll(String today, List<String> projectIdList, int state) {
        return this.mapper.getTodayDataAll(today, state, projectIdList);
    }

    /**
     * 获取今天某个项目的数据
     *
     * @param today     今日
     * @param projectId 项目ID
     * @param state     状态
     * @return 今天某个项目的数据
     */
    public List<StateDto> getTodayDataByProjectId(String today, String projectId, int state) {
        return this.mapper.getTodayDataByProjectId(today, state, projectId);
    }

    /**
     * 获取概览饼状图
     *
     * @param projectId 项目ID
     * @return 概览饼状图
     */
    public List<Map<String, Object>> getProcessPie(String projectId) {
        String today = avicit.bdp.common.utils.DateUtils.getSystemCurrentDate();
        List<Map<String, Object>> list = null;
        List<Map<String, Object>> dataList = Lists.newArrayList();
        if (StringUtils.isBlank(projectId)) {
            //项目空间id集合
            List<String> projectIdList = Lists.newArrayList();
            String userId = ThreadContextHelper.getUserId();
            if (StringUtils.isBlank(userId)) {
                userId = "1";
            }
            List<Project> projectList = projectMapper.getProjectByUserId(userId);
            for (Project project : projectList) {
                projectIdList.add(project.getId());
            }
            if (CollectionUtils.isNotEmpty(projectIdList)) {
                list = this.mapper.getProcessPieAll(today, projectIdList);
            }
        } else {
            list = this.mapper.getProcessPieAllByProjectId(today, projectId);
        }

        Map<String, Object> stateMap = Maps.newHashMap();
        if (list != null && list.size() > 0) {
            for (Map<String, Object> stringObjectMap : list) {
                Integer state = (Integer) stringObjectMap.get("state");
                String name = ExecutionStatus.of(state).getDescp();
                Long value = (Long) stringObjectMap.get("dataCount");
                stateMap.put(name, value);
            }
        }
        List<EnumDto> enumList = EnumUtil.getEnumList(ExecutionStatus.class);
        for (EnumDto enumDto : enumList) {
            Map<String, Object> dataMap = Maps.newHashMap();
            if (stateMap.containsKey(enumDto.getDescp())) {
                dataMap.put("name", enumDto.getDescp());
                dataMap.put("value", MapUtils.getLong(stateMap, enumDto.getDescp(), 0L));
            } else {
                dataMap.put("name", enumDto.getDescp());
                dataMap.put("value", 0L);
            }
            dataList.add(dataMap);
        }
        return dataList;
    }


    /**
     * 查询最新正在运行的流程实例
     *
     * @param processDefinitionId 流程实例ID
     * @return 正在运行的最新的流程实例
     */
    public ProcessInstance queryRunningProcess(String processDefinitionId, ExecutionStatus executionStatus) {
        return this.mapper.queryRunningProcess(processDefinitionId, executionStatus);
    }

    public Long getDataCountByState(String today, List<String> projectIdList, int state) {
        return this.mapper.getDataCountByState(today, projectIdList, state);
    }

    public Long getSlowCountByState(String today, List<String> projectIdList, int state, Long slowTime) {
        return this.mapper.getSlowCountByState(today, projectIdList, state, slowTime);
    }

    /**
     * @param today     today
     * @param projectId projectId
     * @param state     state
     * @return DataCount
     */
    public Long getDataCountByProjectId(String today, String projectId, Integer state) {
        return this.mapper.getDataCountByProjectId(today, projectId, state);
    }

    /**
     * @param today     today
     * @param projectId projectId
     * @param state     state
     * @param slowTime  slowTime
     * @return SlowCount
     */
    public Long getSlowCountByProjectId(String today, String projectId, Integer state, Long slowTime) {
        return this.mapper.getSlowCountByProjectId(today, projectId, state, slowTime);
    }

    /**
     * 查询最后一次运行的流程实例
     *
     * @param processDefinitionId 流程定义ID
     * @return 最后一次运行的流程实例
     */
    public ProcessInstance queryLastOneProcess(String processDefinitionId) {
        return this.mapper.queryLastOneProcess(processDefinitionId);
    }

    public ProcessInstance getSubInstance(String taskInstanceId, String processInstanceId) {
        ProcessInstanceMap query = new ProcessInstanceMap();
        query.setParentTaskInstanceId(taskInstanceId);
        query.setParentProcessInstanceId(processInstanceId);
        ProcessInstanceMap processInstanceMap = processInstanceMapService.selectOne(query);
        if (processInstanceMap == null) {
            throw new BusinessException("节点日志尚未生成，请稍后再试！ ");
        } else {
            return this.mapper.selectByPrimaryKey(processInstanceMap.getProcessInstanceId());
        }
    }

    public List<ProcessInstance> queryByProcessDefineIds(List<String> definitionIdList) {
        return this.mapper.queryByProcessDefineIds(definitionIdList);
    }

    public ProcessInstance getProcessInstanceByTaskId(String taskId) {
        return this.mapper.getProcessInstanceByTaskId(taskId);
    }

    public List<ProcessInstance> queryByCommandStartTime(String processDefinitionId, String startTime) {
        return this.mapper.queryByCommandStartTime(processDefinitionId, startTime);
    }

    /**
     * phm查询运行日志
     *
     * @param    processDefinitionId
     * @param    startTime
     * @param    endTime
     * @param    pageNo
     * @param    pageSize
     * @author felix
     * @date 2022/3/30 15:19
     */
    public QueryRespBean<ProcessInstance> getInstanceList(String processDefinitionId, String startTime,
                                                          String endTime, Integer pageNo, Integer pageSize) {
        QueryRespBean<ProcessInstance> queryRespBean = new QueryRespBean<>();

        PageHelper.startPage(pageNo, pageSize);
        Date start = null;
        Date end = null;
        try {
            if (StringUtils.isNotEmpty(startTime)) {
                start = DateUtils.getScheduleDate(startTime);
            }
            if (StringUtils.isNotEmpty(endTime)) {
                end = DateUtils.getScheduleDate(endTime);
            }
        } catch (Exception e) {
            throw new BusinessException(MessageFormat.format(Status.REQUEST_PARAMS_NOT_VALID_ERROR.getMsg(), "startDate,endDate"));
        }

        Page<ProcessInstance> processInstanceList = mapper.queryProcessInstanceListPaging(processDefinitionId, null,
                null, null, null, start, end);

        List<ProcessInstance> processInstances = processInstanceList.getResult();
        if (CollectionUtils.isNotEmpty(processInstances)) {
            for (ProcessInstance processInstance : processInstances) {
                long endRunTime = processInstance.getEndTime() == null ? System.currentTimeMillis() : processInstance.getEndTime().getTime();
                long duration = (endRunTime - processInstance.getStartTime().getTime()) / 1000;
                processInstance.setDuration(duration);
                String formatTime = DateUtils.formatTimeLong(duration);
                processInstance.setFormatTime(formatTime);
            }
        }
        queryRespBean.setResult(processInstanceList);
        BdpLogUtil.log4Query(queryRespBean);
        return queryRespBean;
    }

    public List<ProcessInstance> getPhmTrainInstanceList(String processDefinitionId) {
        List<ProcessInstance> processInstanceList = this.mapper.getPhmTrainInstanceList(processDefinitionId);
        ProcessInstance processInstance = new ProcessInstance();
        processInstance.setId("last");
        processInstance.setName("最新");
        processInstance.setState(7);
        processInstanceList.add(0,processInstance);
        return processInstanceList;
    }

    public ProcessInstance getLastProcessInstance(String processDefinitionId) {
        return this.mapper.getLastProcessInstance(processDefinitionId);
    }

    public List<ProcessInstance> getInstanceListByStartTime(String startDate) {
        return this.mapper.getInstanceListByStartTime(startDate);
    }

    public List<ProcessInstance> getProcessInstanceByProcessDefinitionId(String processDefinitionId) {
        return this.mapper.getProcessInstanceByProcessDefinitionId(processDefinitionId);
    }
}
